#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<assert.h>
#include"type.h"
#include"interpret.h"

struct table* tytable = NULL;
struct table* valtable = NULL;

struct exptype* newExpType(struct CM_exp* exp, struct valtype* vty)
{
    struct exptype* ptr = (struct exptype*)sec_malloc(sizeof(struct exptype));
    ptr->exp = exp;
    ptr->vty = vty;

    return ptr;
}

struct stmttype* newStmtType(int k, struct exptype* et)
{
    struct stmttype* ptr = (struct stmttype*)sec_malloc(sizeof(struct stmttype));
    ptr->kind = k;
    ptr->et = et;

    return ptr;
}

struct stmttype* interstmt(struct table* tt, struct table* vt, struct CM_stmt* stmt)
{
    struct exptype* et = NULL;
	struct stmttype* sty = NULL;
    int k = 0;
    struct E_enventry* var = NULL;
    struct valtype* val = NULL;
    if(stmt==NULL)
		return NULL;
	
    switch(stmt->kind)
    {
        case cm_exp:
            et = interexp(tt, vt, stmt->u.exp);
            k = stmt_exp;
            break;
        case cm_blockstmt:
            printf("block   %d\n", block++);
			begin_Scope(tt);
			begin_Scope(vt);
			sty = interstmtlist(tt, vt, stmt->u.sl);
            sty->kind = stmt_list;
			end_Scope(tt);
			end_Scope(vt);
			return sty;
        case cm_decl:
            interdecl(tt, vt, stmt->u.de);
            k = stmt_decl;
            break;
        case cm_assign:
            et = interassign(tt, vt, stmt->u.as);
            k = stmt_assign;
            break;
        case cm_if:
            et = interexp(tt, vt, stmt->u.i->cond);
            if(toInt(et->vty) != 0)
                sty=interstmt(tt, vt, stmt->u.i->match);
            else if(stmt->u.i->unmatch != NULL)
                sty=interstmt(tt, vt, stmt->u.i->unmatch);
            else {}
            
            if(sty->kind == stmt_return)
                return sty;
            else
                k = stmt_if;
            break;
        case cm_while:
            et = interexp(tt, vt, stmt->u.w->cond);
            while(toInt(et->vty) != 0)
                sty=interstmt(tt, vt, stmt->u.w->match);
            k = stmt_while;
            break;
        case cm_for:
            et = interassign(tt, vt, stmt->u.f->init);
            et = interexp(tt, vt, stmt->u.f->cond);
            while(toInt(et->vty) != 0){
                sty=interstmt(tt, vt, stmt->u.f->match);
                et = interassign(tt, vt, stmt->u.f->after);
                et = interexp(tt, vt, stmt->u.f->cond);
            }
            k = stmt_for;
            break;
        case cm_return:
            if(stmt->u.exp==NULL)
                et = newExpType(NULL, cm_valtype(cm_ty_tyvoid()));
            else
                et = interexp(tt, vt, stmt->u.exp);
            k = stmt_return;
			break;
        default:
            printf("not support stmt-kind.\n");
    }
    return newStmtType(k, et);
}

struct stmttype* interstmtlist(struct table* tt, struct table* vt, struct CM_stmtlist* list)
{
    struct CM_stmt* st = NULL;
    struct stmttype* sty = NULL;
    assert(list);
    st = list->stm;
    if(list->next==NULL)
	    return interstmt(tt, vt, st);
    else{
	    sty = interstmt(tt, vt, st);
        if(sty->kind == stmt_return)
            return sty;
	    return interstmtlist(tt, vt, list->next);
	}
}

// if exp->vty == NULL infer sth wrong
struct exptype* interexp(struct table* tt, struct table* vt, struct CM_exp* exp)
{
    struct exptype* ptr = (struct exptype*)sec_malloc(sizeof(struct exptype));
    struct exptype* left = NULL;
    struct exptype* right = NULL;
    struct exptype* res = NULL;
    struct E_enventry* var;
    ptr->vty = NULL;
    if(exp == NULL)
        return ptr;
    ptr->exp = exp;

    switch(exp->kind){
        case cm_const_int:
            ptr->vty = convertInt(atoi(exp->u.id->name));
            break;
		case cm_const_float:
            ptr->vty = convertDouble(atof(exp->u.id->name));
            break;
		case cm_const_char:
            ptr->vty = convertChar(exp->u.id->name[1]);
            break;
        case cm_plus:
        case cm_minus:
        case cm_mul:
        case cm_div:
        case cmp_Less:
        case cmp_LE:
        case cmp_Equal:
        case cmp_Great:
        case cmp_GE:
        case cmp_NE:
            left = interexp(tt, vt, exp->u.kid->left);
            right = interexp(tt, vt, exp->u.kid->right);
            if((left->vty!=NULL) && (right->vty!=NULL))
                ptr->vty = calcExp(exp->kind, left, right);
            break;
        case cm_parent:
            res = interexp(tt, vt, exp->u.kid->left);
            ptr->vty = res->vty;
            break;
        case cm_var:
            ptr->vty = intervar(tt, vt, exp->u.var);
			break;
        case cm_funcall:
            begin_Scope(tt);
			begin_Scope(vt);
            ptr->vty = interfuncall(tt, vt, exp->u.fun);
            end_Scope(tt);
            end_Scope(vt);
            break;
        default:
            printf("not support operator.\n");
    }

    return ptr;
}

/*
    caculate a temp var; may memory leak;
*/
struct valtype* calcExp(int op, struct exptype* left, struct exptype* right)
{
    switch(op){
        case cm_plus:
            return  plus_val(left->vty, right->vty);
        case cm_minus:
            return minus_val(left->vty, right->vty);
        case cm_mul:
            return  mul_val(left->vty, right->vty);
        case cm_div:
            return div_val(left->vty, right->vty);
        case cmp_Less:
            return val_cmp_Less(left->vty, right->vty);
        case cmp_LE:
            return val_cmp_LessOrEqual(left->vty, right->vty);
        case cmp_Equal:
            return val_cmp_Equal(left->vty, right->vty);
        case cmp_Great:
            return val_cmp_Greater(left->vty, right->vty);
        case cmp_GE:
            return val_cmp_GreaterOrEqual(left->vty, right->vty);
        case cmp_NE:
            return val_cmp_NotEqual(left->vty, right->vty);
        default:
            printf("unknown operator.\n");
            return NULL;
    }
}

struct exptype* interassign(struct table* tt, struct table* vt, struct CM_assign* as)
{
    struct valtype* var = NULL;
    struct exptype* et = NULL;
    var = intervar(tt, vt, as->var);
    if(var == NULL)
        return ;
    et = interexp(tt, vt, as->exp);
    if(et->vty!=NULL)
        assign_val(var, et->vty);

    return et;
}

struct valtype* interfuncall(struct table* tt, struct table* vt, struct CM_fun_call* funcall)
{
    struct E_enventry* fun = NULL;
    struct E_enventry* var = NULL;
    struct exptype* et = NULL;
    struct stmttype* st = NULL;
    struct CM_varlist* list = NULL;
    struct CM_vardeclist* params = NULL;
    struct CM_vardecl* vd = NULL;
    struct valtype* real = NULL;
    struct valtype* virtual = NULL;
    struct valtypeList* vtyList = NULL;
    struct valtypeList* cur = NULL;
    struct valtypeList* prev = NULL;
    fun = (struct E_enventry*) Symtable_lookup(vt, funcall->funName);
    if(fun==NULL || fun->kind != E_fun)
    {
        printf("%s in not a function.\n", funcall->funName);
        return NULL;
    }
    params = fun->u.fun->params;
    list = funcall->paramlist;
    //已经处理递归问题。

    while(list!=NULL)
    {
        et = interexp(tt, vt, list->exp);
        list = list->next;
        if(et->vty==NULL)
            return NULL;
        vtyList = cm_valtypeList(et->vty, vtyList);
    }

    cur = vtyList;
    prev = NULL;

    //逆置链表
    while(vtyList!=NULL)
    {
        cur = vtyList->next;
        vtyList->next = prev;
        prev = vtyList;
        vtyList = cur;
    }
    vtyList = cur = prev;//获取链首

    while(params!=NULL && cur!=NULL)
    {
        intervardecl(tt, vt, params->stfield);
        if(params->stfield->type->kind != cm_ty_arr)
            virtual = inter_id_var(tt, vt, params->stfield->u.sym);
        else{
            printf("not support array parms now. Sorry\n");
            break;
        }
        real = cur->val;
        assign_val(virtual, real);

        cur = cur->next;
        params = params->next;
    }

    if(params || cur){
        printf("params not enough.\n");
    }else{
        st = interstmtlist(tt, vt, fun->u.fun->stmt);
        if((st->kind != stmt_return) || (st->et->vty==NULL))
            return NULL;
        real = st->et->vty;
        if(real->ty != fun->u.fun->result->ty){
            printf("incompatible types");
        }else{
            return real;
        }
    }
    return NULL;
}


void interdecl(struct table* tt, struct table* vt, struct CM_decl* decl)
{
    if(decl==NULL)
		return;
    switch(decl->kind){
		case cm_typedecl:
			intertypedecl(tt, vt, decl->u.st);
			break;
		case cm_vardecl:
			intervardecl(tt, vt, decl->u.type);
			break;
        case cm_fundecl:
            interfundecl(tt, vt, decl->u.fun);
            break;
		default:
			printf("not support to log.\n");
	}
}
void interfundecl(struct table* tt, struct table* vt, struct CM_fundecl* fun)
{
    struct CM_vardeclist* list = fun->paramlist;
    struct valfieldlist* fdl = NULL;
	struct type* st = NULL;
	struct field* f = NULL;
	if(fun==NULL)
		return ;
    st = (struct type* )Symtable_lookup(tt, fun->type->type);
    if(st==NULL)
    {
        printf("undefined type: %s", fun->type->type->name);
		return ;
    }
    Symtable_insert(vt, fun->funName, E_funEntry(list, cm_valtype(st), fun->stmt));
}
void intertypedecl(struct table* tt, struct table* vt, struct CM_typedecl* type)
{
    struct CM_vardeclist* list = type->list;
	struct fieldlist* fdl = NULL;
	struct type* st = NULL;
	struct field* f = NULL;
	if(type==NULL)
		return ;
	
	while(list)
	{
		st = (struct type* )Symtable_lookup(tt, list->stfield->type->type);
		if(st==NULL)
		{
			printf("undefined type: %s", list->stfield->type->type->name);
			return ;
		}
		f = cm_Field(list->stfield->u.sym, st);
		fdl = cm_FieldList(f, fdl);
		list = list->next;
	}
	Symtable_insert(tt, type->sym, cm_Struct(fdl));
}


void intervardecl(struct table* tt, struct table* vt, struct CM_vardecl* var)
{
    struct type* ty = NULL;
    int size = 0;
    struct exptype* et = NULL;
	if(var==NULL)
		return;
	ty = (struct type* )Symtable_lookup(tt, var->type->type);
	if(ty == NULL)
		{ printf("undefined type %s .\n", var->type->type->name); return; }
	if(var->type->kind != cm_ty_arr){
        Symtable_insert(vt, var->u.sym, E_varEntry(cm_valtype(ty)));
    }
    else{
        et = interexp(tt, vt, var->u.arr->size);
        size = toInt(et->vty);
        Symtable_insert(vt, var->u.arr->sym, E_varEntry(cm_valarray(cm_Array(ty), size)));
    }
}

struct valtype* intervar(struct table* tt, struct table* vt, struct CM_var* var)
{
    switch(var->kind)
    {
        case var_abs_id:
            return inter_id_var(tt, vt, var->u.sym);
        case var_abs_array:
            return inter_array_get_var(tt, vt, var->u.arr);
        case var_abs_struct:
            return inter_struct_get_var(tt, vt, var->u.stu);
        default:
            printf("not support now.\n");
    }
    return NULL;
}

struct valtype* inter_id_var(struct table* tt, struct table* vt, struct symbol* sym)
{
    struct E_enventry* var = NULL;
    var = (struct E_enventry*) Symtable_lookup(vt, sym);
    if(var->kind == E_var)
        return var->u.var;
    else 
        return NULL;
}

struct valtype* inter_array_get_var(struct table* tt, struct table* vt, struct CM_arr_get* ag)
{
    struct exptype* et = NULL;
    int index = 0;
    struct valtype* val = inter_id_var(tt, vt, ag->id);
    if(val==NULL){
        printf("variable %s is not declre.\n", ag->id->name);
        return NULL;
    }else if(val->ty->kind != var_array){
        printf("variable %s can't use as array.\n", ag->id->name);
        return NULL;
    }

    et = interexp(tt, vt, ag->index);
    index = toInt(et->vty);

    if(index<0 || index >= val->u.arr->size)
        printf("%s[%d] :out of array scope!\n", ag->id->name, index);
    else
        return &(val->u.arr->val[index]);

    return NULL;
}

struct valtype* inter_struct_get_var(struct table* tt, struct table* vt, struct CM_struct_get* sg)
{
    struct exptype* et = NULL;
    int index = 0;
    struct valtype* val = inter_id_var(tt, vt, sg->sym);
    struct valtype* fe = NULL;

    if(val==NULL){
        printf("variable %s is not declre.\n", sg->sym->name);
        return NULL;
    }else if(val->ty->kind != var_struct){
        printf("variable %s can't use as struct.\n", sg->sym->name);
        return NULL;
    }
    fe = get_StructField(val, sg->field);
    if(fe == NULL)
        printf("variable %s has not field named %s\n", sg->sym->name, sg->field->name);
    
    return fe;
}

void showStmtValue(struct table* tt, struct table* vt, struct stmttype* st)
{
    if(st==NULL)
        return ;
    switch(st->kind)
    {
        case stmt_list:
        case stmt_decl:
        case stmt_while:
        case stmt_for:
        case stmt_if:
            break;
        case stmt_exp:
        case stmt_assign:
            if(st->et->vty!=NULL)
                showValue(st->et->vty);
            break;
        case stmt_return:
            printf("error use return out of a function scope.\n");
        default:
            printf("stmt type error.\n");
    }
}

int main()
{
	tytable = E_base_type_env();
	valtable = E_base_value_env();
	assert(tytable && valtable);
	printf("> ");
	return yyparse();
}
